Popular Searches
Popular Course Categories
Popular Courses

Creating Interactive Flutter UI

Creating Interactive Flutter UI

Flutter Animations & UI Effects

Creating Interactive Flutter UI

Interactive user interfaces allow users to communicate with a Flutter application through taps, swipes, typing, dragging, selecting, scrolling, gestures, dialogs, animations, and other actions. Flutter provides widgets and state-management mechanisms that make it possible to build responsive and engaging applications.

Flutter UI is built from widgets. When the application's state changes because of user interaction, Flutter can rebuild the relevant part of the widget tree to display the updated interface.


1. What is an Interactive UI?

An interactive UI is a user interface that responds to actions performed by the user. Instead of simply displaying information, an interactive application reacts to input and changes its appearance or behavior.

Common User Interactions

  • Button taps
  • Text input
  • Checkbox and switch selection
  • Radio button selection
  • Dropdown selection
  • Slider movement
  • Scrolling
  • Long press
  • Swipe and drag gestures
  • Pinch and zoom
  • Navigation between screens
  • Showing dialogs, menus, and bottom sheets
  • Animations based on user actions

2. StatelessWidget vs StatefulWidget

Interactive UI frequently requires state. A StatelessWidget is suitable when its appearance does not change based on internal interaction. A StatefulWidget is used when a widget needs mutable state that can change during its lifetime.

Feature StatelessWidget StatefulWidget
State No mutable internal state Can maintain mutable state
UI changes Usually based on external configuration Can change in response to interaction
Example Text, Icon Checkbox, Slider, TextField
setState() Not available Used to trigger rebuilds

3. Understanding State in Interactive UI

State is data that can change while the application is running and affects what the user sees. For example, a favorite button may have two states: favorite and not favorite.

When the state changes, Flutter can rebuild the widget so that the UI represents the new state.

bool isFavorite = false;

After a user taps the favorite button, the value can change:

setState(() {
  isFavorite = !isFavorite;
});

4. Using setState() for Interactive UI

setState() is commonly used for simple local widget state. It tells Flutter that the state has changed and that the widget should rebuild.

Example: Interactive Favorite Button

import 'package:flutter/material.dart';

class FavoriteButton extends StatefulWidget {
  const FavoriteButton({super.key});

  @override
  State createState() => _FavoriteButtonState();
}

class _FavoriteButtonState extends State {
  bool isFavorite = false;

  @override
  Widget build(BuildContext context) {
    return IconButton(
      icon: Icon(
        isFavorite ? Icons.favorite : Icons.favorite_border,
        color: isFavorite ? Colors.red : Colors.grey,
      ),
      onPressed: () {
        setState(() {
          isFavorite = !isFavorite;
        });
      },
    );
  }
}

How It Works

  1. The initial value of isFavorite is false.
  2. The outline heart is displayed.
  3. The user taps the button.
  4. setState() changes the value.
  5. Flutter rebuilds the widget.
  6. The filled heart is displayed.

5. Buttons in Interactive Flutter UI

Flutter provides several buttons for common interactions.

  • ElevatedButton
  • TextButton
  • OutlinedButton
  • IconButton
  • FloatingActionButton

ElevatedButton Example

ElevatedButton(
  onPressed: () {
    print('Button pressed');
  },
  child: const Text('Click Me'),
)

TextButton Example

TextButton(
  onPressed: () {
    print('Text button pressed');
  },
  child: const Text('Learn More'),
)

IconButton Example

IconButton(
  icon: const Icon(Icons.settings),
  onPressed: () {
    print('Settings opened');
  },
)

FloatingActionButton Example

FloatingActionButton(
  onPressed: () {
    print('Add action');
  },
  child: const Icon(Icons.add),
)

6. Handling User Input with TextField

TextField allows users to enter text. It is commonly used for login forms, search fields, registration forms, comments, and other input areas.

TextField(
  decoration: const InputDecoration(
    labelText: 'Enter your name',
    border: OutlineInputBorder(),
  ),
)

Using TextEditingController

final TextEditingController nameController = TextEditingController();
TextField(
  controller: nameController,
  decoration: const InputDecoration(
    labelText: 'Name',
  ),
)

The entered value can be accessed using:

String name = nameController.text;

When a controller is created inside a stateful widget, remember to dispose of it:

@override
void dispose() {
  nameController.dispose();
  super.dispose();
}

7. Handling Checkbox Interaction

A Checkbox allows users to select or clear a boolean option.

bool accepted = false;
Checkbox(
  value: accepted,
  onChanged: (value) {
    setState(() {
      accepted = value ?? false;
    });
  },
)

Practical Example

Row(
  children: [
    Checkbox(
      value: accepted,
      onChanged: (value) {
        setState(() {
          accepted = value ?? false;
        });
      },
    ),
    const Expanded(
      child: Text('I accept the terms and conditions'),
    ),
  ],
)

8. Using Switch for Interactive Settings

A Switch is useful when users need to enable or disable a setting.

bool notificationsEnabled = true;
Switch(
  value: notificationsEnabled,
  onChanged: (value) {
    setState(() {
      notificationsEnabled = value;
    });
  },
)

Common Use Cases

  • Dark mode
  • Notifications
  • Location services
  • Auto-save
  • Sound settings
  • Privacy settings

9. Radio Buttons

Radio buttons are useful when the user must choose one option from a group.

String selectedPlan = 'Basic';
Column(
  children: [
    RadioListTile(
      title: const Text('Basic'),
      value: 'Basic',
      groupValue: selectedPlan,
      onChanged: (value) {
        setState(() {
          selectedPlan = value!;
        });
      },
    ),
    RadioListTile(
      title: const Text('Premium'),
      value: 'Premium',
      groupValue: selectedPlan,
      onChanged: (value) {
        setState(() {
          selectedPlan = value!;
        });
      },
    ),
  ],
)

10. Dropdown Selection

A dropdown allows users to select one value from a list of available choices.

String selectedCity = 'Mumbai';
DropdownButton(
  value: selectedCity,
  items: const [
    DropdownMenuItem(
      value: 'Mumbai',
      child: Text('Mumbai'),
    ),
    DropdownMenuItem(
      value: 'Delhi',
      child: Text('Delhi'),
    ),
    DropdownMenuItem(
      value: 'Pune',
      child: Text('Pune'),
    ),
  ],
  onChanged: (value) {
    setState(() {
      selectedCity = value!;
    });
  },
)

11. Slider Interaction

A Slider lets users select a numeric value by moving a thumb along a track.

double volume = 50;
Slider(
  value: volume,
  min: 0,
  max: 100,
  divisions: 10,
  label: volume.round().toString(),
  onChanged: (value) {
    setState(() {
      volume = value;
    });
  },
)

Common Slider Applications

  • Volume control
  • Brightness
  • Price range
  • Progress selection
  • Rating or percentage selection

12. GestureDetector

GestureDetector detects gestures performed by the user. It can recognize taps, long presses, drags, and other gestures.

GestureDetector(
  onTap: () {
    print('Container tapped');
  },
  child: Container(
    width: 200,
    height: 100,
    color: Colors.blue,
    child: const Center(
      child: Text(
        'Tap Me',
        style: TextStyle(color: Colors.white),
      ),
    ),
  ),
)

Common GestureDetector Callbacks

Callback Purpose
onTap Detects a normal tap
onDoubleTap Detects a double tap
onLongPress Detects a long press
onPanUpdate Detects movement in any direction
onHorizontalDragUpdate Detects horizontal dragging
onVerticalDragUpdate Detects vertical dragging
onScaleUpdate Detects scaling gestures

13. InkWell for Material Tap Effects

InkWell provides a Material-style visual response when a user interacts with a widget, such as a ripple effect.

InkWell(
  onTap: () {
    print('Card tapped');
  },
  child: Container(
    padding: const EdgeInsets.all(20),
    child: const Text('Interactive Card'),
  ),
)

InkWell is useful when you want both interaction and visual feedback.


14. Interactive Cards

Cards are frequently used in dashboards, shopping applications, profile screens, and content-based applications.

Card(
  child: InkWell(
    onTap: () {
      print('Product selected');
    },
    child: const Padding(
      padding: EdgeInsets.all(20),
      child: Row(
        children: [
          Icon(Icons.shopping_bag),
          SizedBox(width: 12),
          Text('Product Details'),
        ],
      ),
    ),
  ),
)

15. Interactive Lists

List items can respond to taps using ListTile, InkWell, or GestureDetector.

ListTile(
  leading: const Icon(Icons.person),
  title: const Text('Profile'),
  subtitle: const Text('View your profile'),
  trailing: const Icon(Icons.arrow_forward_ios),
  onTap: () {
    print('Profile selected');
  },
)

16. Dismissible Widgets

Dismissible allows users to remove or dismiss an item by swiping it.

Dismissible(
  key: const ValueKey('item-1'),
  onDismissed: (direction) {
    print('Item dismissed');
  },
  background: Container(
    color: Colors.red,
    child: const Icon(Icons.delete),
  ),
  child: const ListTile(
    title: Text('Swipe to delete'),
  ),
)

Common Use Cases

  • Delete an email
  • Remove a shopping-cart item
  • Dismiss a notification
  • Remove a task from a list

17. InteractiveViewer

InteractiveViewer can provide pan and zoom interactions for content.

InteractiveViewer(
  minScale: 0.5,
  maxScale: 4.0,
  child: Image.asset('assets/map.png'),
)

This is useful for maps, diagrams, large images, floor plans, and documents.


18. Showing Dialogs

Dialogs allow an application to temporarily request attention or input from the user.

showDialog(
  context: context,
  builder: (context) {
    return AlertDialog(
      title: const Text('Delete Item'),
      content: const Text(
        'Are you sure you want to delete this item?',
      ),
      actions: [
        TextButton(
          onPressed: () {
            Navigator.pop(context);
          },
          child: const Text('Cancel'),
        ),
        ElevatedButton(
          onPressed: () {
            Navigator.pop(context);
          },
          child: const Text('Delete'),
        ),
      ],
    );
  },
);

19. SnackBar Feedback

A SnackBar provides short feedback after an action.

ScaffoldMessenger.of(context).showSnackBar(
  const SnackBar(
    content: Text('Item added successfully'),
  ),
);

SnackBars are useful for confirmations, errors, and temporary status messages.


20. Bottom Sheets

A bottom sheet displays additional actions or information from the bottom of the screen.

showModalBottomSheet(
  context: context,
  builder: (context) {
    return SafeArea(
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          ListTile(
            leading: const Icon(Icons.camera_alt),
            title: const Text('Camera'),
            onTap: () {
              Navigator.pop(context);
            },
          ),
          ListTile(
            leading: const Icon(Icons.photo),
            title: const Text('Gallery'),
            onTap: () {
              Navigator.pop(context);
            },
          ),
        ],
      ),
    );
  },
);

21. Interactive Forms

Flutter provides Form and FormField widgets for collecting and validating user input.

final formKey = GlobalKey();
Form(
  key: formKey,
  child: Column(
    children: [
      TextFormField(
        decoration: const InputDecoration(
          labelText: 'Email',
        ),
        validator: (value) {
          if (value == null || value.isEmpty) {
            return 'Please enter your email';
          }
          return null;
        },
      ),
      const SizedBox(height: 16),
      ElevatedButton(
        onPressed: () {
          if (formKey.currentState!.validate()) {
            print('Form submitted');
          }
        },
        child: const Text('Submit'),
      ),
    ],
  ),
)

22. Navigation as an Interaction

Navigation allows users to move between screens. Flutter's Navigator maintains a stack of routes and provides methods such as push() and pop().

Navigate to Another Screen

ElevatedButton(
  onPressed: () {
    Navigator.of(context).push(
      MaterialPageRoute(
        builder: (context) => const DetailsScreen(),
      ),
    );
  },
  child: const Text('Open Details'),
)

Return to the Previous Screen

Navigator.pop(context);

23. Interactive Navigation Bar

Navigation components can change the visible content when users select different destinations.

int selectedIndex = 0;
NavigationBar(
  selectedIndex: selectedIndex,
  onDestinationSelected: (index) {
    setState(() {
      selectedIndex = index;
    });
  },
  destinations: const [
    NavigationDestination(
      icon: Icon(Icons.home_outlined),
      selectedIcon: Icon(Icons.home),
      label: 'Home',
    ),
    NavigationDestination(
      icon: Icon(Icons.person_outline),
      selectedIcon: Icon(Icons.person),
      label: 'Profile',
    ),
  ],
)

24. Interactive Tabs

TabBar and TabBarView can be used to switch between related sections.

DefaultTabController(
  length: 2,
  child: Scaffold(
    appBar: AppBar(
      bottom: const TabBar(
        tabs: [
          Tab(text: 'Products'),
          Tab(text: 'Reviews'),
        ],
      ),
    ),
    body: const TabBarView(
      children: [
        Center(child: Text('Products')),
        Center(child: Text('Reviews')),
      ],
    ),
  ),
)

25. Animated Interactive UI

Animations can make interactions easier to understand and provide visual feedback. Flutter includes implicit animation widgets such as AnimatedContainer, AnimatedOpacity, AnimatedScale, and AnimatedPositioned, along with explicit animation widgets such as FadeTransition, SlideTransition, ScaleTransition, and RotationTransition.

AnimatedContainer Example

bool expanded = false;
AnimatedContainer(
  duration: const Duration(milliseconds: 400),
  width: expanded ? 300 : 150,
  height: expanded ? 200 : 100,
  color: expanded ? Colors.blue : Colors.grey,
  child: Center(
    child: ElevatedButton(
      onPressed: () {
        setState(() {
          expanded = !expanded;
        });
      },
      child: const Text('Animate'),
    ),
  ),
)

26. Interactive Opacity

AnimatedOpacity can smoothly show or hide content based on state.

bool visible = true;
AnimatedOpacity(
  opacity: visible ? 1.0 : 0.0,
  duration: const Duration(milliseconds: 300),
  child: const Text('Interactive Content'),
)

27. Interactive Scale

An interactive scale effect can make a button or card appear to respond when selected.

AnimatedScale(
  scale: selected ? 1.1 : 1.0,
  duration: const Duration(milliseconds: 200),
  child: const Icon(Icons.favorite, size: 50),
)

28. Combining Interaction and State

A common pattern is to capture an event, update state, and rebuild the UI.

void _toggle() {
  setState(() {
    isActive = !isActive;
  });
}

The general flow is:

  1. User performs an action.
  2. Flutter detects the interaction.
  3. A callback is executed.
  4. The application updates its state.
  5. Flutter rebuilds the relevant UI.
  6. The user sees the new result.

29. Interactive UI with Conditional Rendering

Conditional expressions can display different widgets depending on the current state.

Column(
  children: [
    Text(isLoggedIn ? 'Welcome Back' : 'Please Login'),
    if (isLoggedIn)
      ElevatedButton(
        onPressed: logout,
        child: const Text('Logout'),
      )
    else
      ElevatedButton(
        onPressed: login,
        child: const Text('Login'),
      ),
  ],
)

30. Loading, Success, Error, and Empty States

Interactive applications frequently communicate the status of asynchronous operations.

if (isLoading) {
  return const Center(
    child: CircularProgressIndicator(),
  );
}

if (hasError) {
  return const Center(
    child: Text('Something went wrong'),
  );
}

if (items.isEmpty) {
  return const Center(
    child: Text('No items available'),
  );
}

return ListView(
  children: items.map((item) {
    return ListTile(
      title: Text(item),
    );
  }).toList(),
);

31. Building a Complete Interactive Counter

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: const CounterScreen(),
    );
  }
}

class CounterScreen extends StatefulWidget {
  const CounterScreen({super.key});

  @override
  State createState() => _CounterScreenState();
}

class _CounterScreenState extends State {
  int count = 0;

  void increment() {
    setState(() {
      count++;
    });
  }

  void decrement() {
    setState(() {
      if (count > 0) {
        count--;
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Interactive Counter'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(
              '$count',
              style: const TextStyle(
                fontSize: 48,
                fontWeight: FontWeight.bold,
              ),
            ),
            const SizedBox(height: 20),
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                IconButton(
                  onPressed: decrement,
                  icon: const Icon(Icons.remove),
                ),
                IconButton(
                  onPressed: increment,
                  icon: const Icon(Icons.add),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

32. Building an Interactive Product Card

class ProductCard extends StatefulWidget {
  const ProductCard({super.key});

  @override
  State createState() => _ProductCardState();
}

class _ProductCardState extends State {
  bool isFavorite = false;

  @override
  Widget build(BuildContext context) {
    return Card(
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          children: [
            const Icon(
              Icons.shopping_bag,
              size: 80,
            ),
            const Text(
              'Flutter Course',
              style: TextStyle(
                fontSize: 20,
                fontWeight: FontWeight.bold,
              ),
            ),
            const Text('Learn Flutter development'),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                ElevatedButton(
                  onPressed: () {
                    ScaffoldMessenger.of(context).showSnackBar(
                      const SnackBar(
                        content: Text('Course added to cart'),
                      ),
                    );
                  },
                  child: const Text('Add to Cart'),
                ),
                IconButton(
                  onPressed: () {
                    setState(() {
                      isFavorite = !isFavorite;
                    });
                  },
                  icon: Icon(
                    isFavorite
                        ? Icons.favorite
                        : Icons.favorite_border,
                  ),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

33. Interactive UI Architecture

For small widgets, local state with setState() can be sufficient. As applications become larger, state may need to be shared between multiple widgets or managed using solutions such as Provider, Riverpod, Bloc, or other state-management approaches.

Typical Interaction Architecture

User Action
    ↓
Gesture / Button / Input
    ↓
Callback
    ↓
State Update
    ↓
Business Logic
    ↓
UI Rebuild
    ↓
Updated Screen

34. Local State vs Shared State

State Type Example Typical Approach
Local UI state Selected tab setState()
Form state Text fields and validation Form, controllers, local state
Shared application state Shopping cart Provider, Riverpod, Bloc, etc.
Remote state API/Firebase data Repository and state-management solution

35. Interactive UI Best Practices

  • Use the simplest suitable interaction widget.
  • Provide clear visual feedback after user actions.
  • Use setState() for small local state.
  • Keep business logic separate from complex UI code.
  • Use meaningful labels for interactive controls.
  • Do not make the entire screen interactive when only a small area needs interaction.
  • Validate user input before processing it.
  • Show loading indicators for longer operations.
  • Display useful error and empty states.
  • Dispose controllers when they are no longer needed.
  • Avoid unnecessary widget rebuilds.
  • Use animations purposefully rather than adding animation everywhere.
  • Consider touch targets and accessibility when designing interactive controls.

36. Common Mistakes

  • Changing state without calling setState() when local widget state needs rebuilding.
  • Calling setState() after a widget has been disposed.
  • Forgetting to dispose TextEditingController or other controllers.
  • Putting large amounts of business logic directly inside build().
  • Creating unnecessarily complex gesture handling.
  • Providing no feedback after an important user action.
  • Ignoring loading, error, and empty states.
  • Using too many animations that distract from the main task.
  • Making interactive elements difficult to discover.

37. Practical Interactive UI Project

A useful practice project is an interactive shopping application.

Suggested Features

  • Product list
  • Product detail screen
  • Favorite button
  • Add-to-cart button
  • Quantity increment and decrement
  • Search field
  • Category selection
  • Price slider
  • Product filtering
  • Swipe-to-delete cart items
  • Navigation between screens
  • Animated product selection
  • Checkout form
  • Success and error feedback

Example Interaction Flow

Product List
    ↓
Tap Product
    ↓
Product Details
    ↓
Select Quantity
    ↓
Add to Cart
    ↓
Show SnackBar
    ↓
Open Cart
    ↓
Checkout Form
    ↓
Validate Form
    ↓
Show Confirmation

38. Interactive Flutter UI Checklist

Requirement Flutter Feature
Button interaction ElevatedButton, TextButton, IconButton
Custom tap interaction GestureDetector
Ripple feedback InkWell
Text input TextField, TextFormField
Boolean selection Checkbox, Switch
Single selection Radio, Dropdown
Numeric selection Slider
Swipe interaction Dismissible
Pan and zoom InteractiveViewer
Temporary feedback SnackBar
Confirmation AlertDialog
Additional actions BottomSheet
Screen navigation Navigator
Animated response AnimatedContainer, AnimatedOpacity, transitions

39. Interview Questions

Q1. What is an interactive UI in Flutter?

An interactive UI responds to user actions such as taps, typing, gestures, selections, scrolling, and navigation.

Q2. What is the difference between StatelessWidget and StatefulWidget?

A StatelessWidget does not maintain mutable internal state, while a StatefulWidget has a separate State object that can store changing values and trigger UI updates.

Q3. What does setState() do?

setState() informs Flutter that local state has changed and that the widget should rebuild to reflect the updated state.

Q4. What is GestureDetector?

GestureDetector detects gestures such as taps, long presses, drags, and scaling gestures.

Q5. What is InkWell?

InkWell provides Material-style interaction feedback such as a ripple effect and supports tap-related callbacks.

Q6. How can a Flutter application validate user input?

Flutter provides Form and FormField-based widgets such as TextFormField, which can use validator functions.

Q7. How can animations improve interactive UI?

Animations can provide visual feedback, communicate changes, guide attention, and make transitions between UI states easier to understand.


40. Quick Revision

  • Interactive UI responds to user actions.
  • State determines how dynamic widgets appear.
  • setState() is useful for simple local state.
  • Buttons provide common interaction patterns.
  • TextField collects user input.
  • Checkbox, Switch, Radio, and Dropdown support selection.
  • Slider supports numeric selection.
  • GestureDetector handles custom gestures.
  • InkWell provides Material interaction feedback.
  • Dismissible supports swipe-based actions.
  • InteractiveViewer supports pan and zoom.
  • Dialog, SnackBar, and BottomSheet communicate with users.
  • Navigator supports screen-to-screen interaction.
  • Animations can make state changes and navigation more understandable.
  • For larger applications, use an appropriate state-management architecture.

41. Learning Outcome

After completing this topic, you should be able to create Flutter interfaces that respond to taps, text input, selections, gestures, scrolling, navigation, dialogs, and other user actions. You should also understand how state changes drive UI updates and how interactive widgets can be combined to create practical application screens.


42. JustAcademy Flutter Resources

Learn more about Flutter development through the JustAcademy Flutter Training Course.

You can also Register for Flutter Course Demo to explore the training program.


43. Summary

Creating an interactive Flutter UI involves combining widgets, state, callbacks, gestures, forms, navigation, feedback components, and animations. Flutter provides ready-made interactive widgets as well as flexible tools such as GestureDetector for custom interactions. By managing state correctly and providing clear feedback, developers can build responsive, user-friendly, and engaging Flutter applications.

whatsapp